/*
 * Copyright 2002-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.security.config.annotation.web.configuration;

import java.util.Collections;
import java.util.List;
import java.util.Map;

import jakarta.servlet.Filter;

import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor;
import org.springframework.security.context.DelegatingApplicationListener;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

/**
 * Uses a {@link WebSecurity} to create the {@link FilterChainProxy} that performs the web
 * based security for Spring Security. It then exports the necessary beans. Customizations
 * can be made to {@link WebSecurity} by implementing {@link WebSecurityConfigurer} and
 * exposing it as a {@link Configuration} or exposing a {@link WebSecurityCustomizer}
 * bean. This configuration is imported when using {@link EnableWebSecurity}.
 * 这个类的作用就是用来创建FilterChainProxy，FilterChainProxy是一个Servlet Filter，他是一组SecurityFilterChain的代理，用于管理这些SecurityFilterChain
 * @author Rob Winch
 * @author Keesun Baik
 * @since 3.2
 * @see EnableWebSecurity
 * @see WebSecurity
 */
@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
    //springSeurity的filterChainProxy的建造器（建造者模式）
	private WebSecurity webSecurity;
    //标记是否开启debug模式，来自注解@EnableWebSecurity的属性debug
	private Boolean debugEnabled;
    //springSecurity的配置类列表
	private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
    //springSecurity的核心过滤器链的列表(里面包含两个属性，匹配请求样式，以及filter过滤器执行链集合)
	//当访问请求到达时会调用该接口的match方法进行请求的匹配，如果返回true则会调用该接口的getFilters方法获取执行链
	private List<SecurityFilterChain> securityFilterChains = Collections.emptyList();
    //用户自定义webSecurity的配置类列表（5.4版本之后新增的配置类）
	private List<WebSecurityCustomizer> webSecurityCustomizers = Collections.emptyList();
    //类加载器
	private ClassLoader beanClassLoader;
    //对象后处理器（这里依赖注入的是AutowireBeanFactoryObjectPostProcessor，该类主要是执行spring bean生命周期）    注意：候选者的属性再@Bean中
	@Autowired(required = false)
	private ObjectPostProcessor<Object> objectObjectPostProcessor;
    //表示忽略当前要注入的bean，如果有直接注入{这里的有是指ioc容器可以生成该bean对象}，没有{指ioc容器初始化完成后该bean对象也不存在}跳过，不会报错。
	//而HttpSecurity的工厂方法是存放在HttpSecurityConfiguration类中，所以会先进行HttpSecurityConfiguration的bean生命周期
	@Autowired(required = false)
	private HttpSecurity httpSecurity;
    //注册了一个委托类型的监听器，用于管理所有的鉴权事件 内部其实就是一个很简单的把监听器又嵌套了一层，在内部for循环发送事件,这样我们就可以注册内部的监听器
	@Bean
	public static DelegatingApplicationListener delegatingApplicationListener() {
		return new DelegatingApplicationListener();
	}
    // 权限表达式的支持
	@Bean
	@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler() {
		return this.webSecurity.getExpressionHandler();
	}

	/**
	 * Creates the Spring Security Filter Chain
	 * springSecurity过滤器链声明,最终返回一个FilterChainProxy对象，而该对象中的List<SecurityFilterChain> filterChains属性则封装了所有的过滤器链，循环遍历执行。
	 * (DelegatingFilterProxy:这个类啥也不干，其是springFramework提供的，就是把自己持有的bean名称从spring容器中拿出来，拿出来的就是个Filter，然后让这个Filter
	 * 去执行执行自己的doFilter方法，再springSecurity的作用就是承上启下，在执行spring提供的filter时，其会获取名称为springSecurityFilterChain对应的bean对象，即当前
	 * bean方法放回的filterChainProxy对象，执行其doFilter方法，再该方法中遍历执行封装的过滤器链)
	 * DelegatingFilterProxy何时创建初始化bean名称:SecurityFilterAutoConfiguration.class(springBoot自动装配 spring.factories 或者
	 * AutoConfiguration.import)会创建DelegatingFilterProxyRegistrationBean实体，其实现了ServletContextInitializer
	 * 接口，注意这个ServletContextInitializer接口是org.springframework.boot.web.servlet.ServletContextInitializer是springBoot定义的，
	 * 而非jakarta.servlet.ServletContextInitializer接口，该接口是由tomcat定义的，会绑定到Tomcat中的context组件中，再context start过程中会调用绑定到
	 * context组件的ServletContextInitializer执行其onStartup方法
	 * 从而将DelegatingFilterProxyRegistrationBean实体中包装的DelegatingFilterProxy过滤器对象绑定到context组件上
	 * 两个ServletContextInitializer接口并不一样，但是基本上是在相同的位置被调用，这离不开一个至关重要的的类TomcatStarter
	 * @return the {@link Filter} that represents the security filter chain
	 * @throws Exception
	 */
	@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public Filter springSecurityFilterChain() throws Exception {
		// 判断是否有自定义配置的SecurityFilterChain，经过下面的自动注入，此时该集合中应该存在一个由ClientServiceConfig配置类引入的@bean
		// 方法生成的一个SecurityFilterChain对象 即DefaultSecurityFilterChain 这样的话通过http.build()方法构建的执行链就和webSecurity构建的filterChainProxy绑定了关系
		// 我们可以定义多个SecurityFilterChain对应的@bean对象，其filter设置不一样，其请求匹配器requestMatcher中封装了匹配请求的样式不一样就会达到不同的请求执行不同的filter执行链
		boolean hasFilterChain = !this.securityFilterChains.isEmpty();
		//当过滤器链没有的时候注册一个默认的  默认不执行
		if (!hasFilterChain) {
			//在filterChainProxy的建造器中增加一个默认的securityFilterChain的构建器
			this.webSecurity.addSecurityFilterChainBuilder(() -> {
				this.httpSecurity.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated());
				this.httpSecurity.formLogin(Customizer.withDefaults());
				this.httpSecurity.httpBasic(Customizer.withDefaults());
				//默认情况下返回一个DefaultSecurityFilterChain(securityFilterChain实现类)
				return this.httpSecurity.build();
			});
		}
		//设置webSecurity的拦截器链的构建器，通俗讲就是将生成的DefaultSecurityFilterChain一个个的放入到DefaultSecurityFilterChain的建造者中
		for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
			this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
		}
		//用户的一些自定义配置webSecurity
		for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
			//在这里才会真正的调用WebSecurityCustomizer实现类对象返回的lambda表达式，从而可以对webSecurity进行调整，比如增加忽略路径属性等
			customizer.customize(this.webSecurity);
		}
		// 通过WebSecurity建造器构造出一个名字为springSecurityFilterChain 类型为FilterChainProxy的Filter
		// 这个Filter最终会被spring提供的DelegatingFilterProxy代理到Servlet容器中  注意：默认情况下webSecurity对象中的configurers为null，除非我们自定义了
		// WebSecurityConfigurer接口的实现类注入到容器中  会在下面的setFilterChainProxySecurityConfigurer方法中填充到configurers集合中。 其目的是在我们
		//返回真正的目标对象之前执行其init和config方法来对webSecurity对象进行修改。
		return this.webSecurity.build();
	}

	/**
	 * Creates the {@link WebInvocationPrivilegeEvaluator} that is necessary to evaluate
	 * privileges for a given web URI
	 * 权限评估器。对于JSP-tag支持是必须的
	 * @return the {@link WebInvocationPrivilegeEvaluator}
	 */
	@Bean
	@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public WebInvocationPrivilegeEvaluator privilegeEvaluator() {
		return this.webSecurity.getPrivilegeEvaluator();
	}

	/**
	 * Sets the {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>}
	 * instances used to create the web configuration.
	 * 获取并设置容器中已经加载的所有WebSecurityConfigurer实例用于配置，初始化一个WebSecurity
	 * @param objectPostProcessor the {@link ObjectPostProcessor} used to create a
	 * {@link WebSecurity} instance
	 * @param beanFactory the bean factory to use to retrieve the relevant
	 * {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>} instances used to
	 * create the web configuration
	 * @throws Exception
	 */
	// 在这个方法中会创建一个WebSecurity实例，然后获取容器中WebSecurityConfigurer接口的所有实现类设置到WebSecurity实例中。
	// 同时初始化了当前配置类的两个属性值webSecurity和webSecurityConfigurers。参数objectPostProcessor:AutowireBeanFactoryObjectPostProcessor
	@Autowired(required = false)
	public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor,
			ConfigurableListableBeanFactory beanFactory) throws Exception {
		//1、初始化一个WebSecurity实例，并将其赋值到类属性webSecurity上
		this.webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
		//2、设置是否开启debug模式,默认情况下debugEnabled为null，会再下面的setImportMetadata方法中重新赋值webSecurity中的该属性信息
		if (this.debugEnabled != null) {
			this.webSecurity.debug(this.debugEnabled);
		}
		// 初始化一个AutowiredWebSecurityConfigurersIgnoreParents实例，用于加载容器中的所有WebSecurityConfigurer实例，进而填充到webSecurity
		// 中的configurers属性集合中
		List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new AutowiredWebSecurityConfigurersIgnoreParents(
				beanFactory)
			.getWebSecurityConfigurers();
		//3、根据@Order注解排序，然后检测配置排序是否有重复
		webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE);
		Integer previousOrder = null;
		Object previousConfig = null;
		for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
			//AnnotationAwareOrderComparator是本类中的一个内部类，获取order中的value值
			Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
			//挨个循环遍历跟上个进行比较，其value值不能一样，否则异常抛出
			if (previousOrder != null && previousOrder.equals(order)) {
				throw new IllegalStateException("@Order on WebSecurityConfigurers must be unique. Order of " + order
						+ " was already used on " + previousConfig + ", so it cannot be used on " + config + " too.");
			}
			previousOrder = order;
			previousConfig = config;
		}
		//4、将配置添加到webSecurity中
		for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
			//将配置类初始化到AbstractConfiguredSecurityBuilder(WebSecurity的父类)中的configurers属性中
			//注意：WebSecurity和HttpSecurity均是AbstractConfiguredSecurityBuilder的子类  两者在实例化后均会将各自的配置类设置到其父类configurers属性中
			this.webSecurity.apply(webSecurityConfigurer);
		}
		// 5、将配置类列表复制到类属性webSecurityConfigurers上
		this.webSecurityConfigurers = webSecurityConfigurers;
	}
    //依赖注入所有的SecurityFilterChain 默认仅有一个DefaultSecurityFilterChain 通过ClientServiceConfig配置类注入进来的 我们可进行自定义扩展 承上启下比较重要
	//再结合上面的filterChainProxy bean对象的生成 会将其绑定到filterChainProxy对象中的List<SecurityFilterChain> filterChains属性集合中 然后请求到达后
	//经过DelegatingFilterProxy->FilterChainProxy实现了filter接口，再其执行的时候会循环遍历filterChains属性集合，然后根据DefaultSecurityFilterChain中的
	//请求匹配器requestMatcher来决定是否使用该SecurityFilterChain中的执行链进行处理
	@Autowired(required = false)
	void setFilterChains(List<SecurityFilterChain> securityFilterChains) {
		this.securityFilterChains = securityFilterChains;
	}
	// 注入容器中所有用户自定义的WebSecurityCustomizer，用于自定义WebSecurity  将自定义的注入到容器后该方法会自动获取进行注入操作
	// 1:我们可以直接定义WebSecurityCustomizer的实现类，注入到容器
	// 2:也可以在配置类中通过@bean的方式进行自定义 比如ClientServiceConfig配置类中定义的@bean修饰ignoringCustomizer的方法
	// 注意该bean对象是一个lambda表达式，真正的执行是在FilterChainProxy对象生成的方法中
	@Autowired(required = false)
	void setWebSecurityCustomizers(List<WebSecurityCustomizer> webSecurityCustomizers) {
		this.webSecurityCustomizers = webSecurityCustomizers;
	}
    //钩子接口，用于修改BeanFactory
	@Bean
	public static BeanFactoryPostProcessor conversionServicePostProcessor() {
		return new RsaKeyConversionServicePostProcessor();
	}
    // 获取注解的日志级别并设置(ImportAware接口的方法，会在当前bean初始化的前置方法中进行调用
	// ImportAwareBeanPostProcessor实现了BeanPostProcessor接口，before方法中调用)
	// importMetadata:导入类元数据信息
	@Override
	public void setImportMetadata(AnnotationMetadata importMetadata) {
		//默认只有WebSecurityEnablerConfiguration导入了此类，而此类一定标志了@EnableWebSecurity
		Map<String, Object> enableWebSecurityAttrMap = importMetadata
			.getAnnotationAttributes(EnableWebSecurity.class.getName());
		AnnotationAttributes enableWebSecurityAttrs = AnnotationAttributes.fromMap(enableWebSecurityAttrMap);
		this.debugEnabled = enableWebSecurityAttrs.getBoolean("debug");
		if (this.webSecurity != null) {
			this.webSecurity.debug(this.debugEnabled);
		}
	}
    //这个接口可以使得当前配置类获取到加载他的类加载器 设置类加载器（BeanClassLoaderAware接口的方法）
	@Override
	public void setBeanClassLoader(ClassLoader classLoader) {
		this.beanClassLoader = classLoader;
	}

	/**
	 * A custom version of the Spring provided AnnotationAwareOrderComparator that uses
	 * {@link AnnotationUtils#findAnnotation(Class, Class)} to look on super class
	 * instances for the {@link Order} annotation.
	 * 一个内部类用于获取类上的@Order注解，并提供比较的功能 获取类上的Order注解的值
	 * @author Rob Winch
	 * @since 3.2
	 */
	private static class AnnotationAwareOrderComparator extends OrderComparator {

		private static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator();

		@Override
		protected int getOrder(Object obj) {
			return lookupOrder(obj);
		}

		private static int lookupOrder(Object obj) {
			if (obj instanceof Ordered) {
				return ((Ordered) obj).getOrder();
			}
			if (obj != null) {
				Class<?> clazz = ((obj instanceof Class) ? (Class<?>) obj : obj.getClass());
				Order order = AnnotationUtils.findAnnotation(clazz, Order.class);
				if (order != null) {
					return order.value();
				}
			}
			return Ordered.LOWEST_PRECEDENCE;
		}

	}

}
